package halftone.errdiff.pipeline

import chisel3._
import chisel3.util.{Counter, Decoupled}
import halftone.ErrDiffConfig
import halftone.errdiff.ErrDiffCorePipParam._
import tools.bus.BramNativePortFull

class ErrorInPip(config: ErrDiffConfig) extends Module {
  val io = IO(new Bundle {
    val in    = Flipped(Decoupled(new PixelGet2ErrorInPip(config.pixelWidth, config.posWidth)))
    val pb    = Flipped(new BramNativePortFull(config.bramDataBits, config.bramAddrBits))
    val out   = Decoupled(new ErrorIn2ThreshCalcPip(config.pixelWidth, config.posWidth, config.errorWidth))
    val err_r = Input(Vec(2, SInt(config.errorWidth.W)))
    val stop = Input(Bool())
  })

  // Registers(for value storage and state presentation)
  val pos         = Reg(UInt(config.posWidth.W))
  val pix         = Reg(UInt(config.pixelWidth.W))
  val errIn       = Reg(Vec(4, SInt(config.errorWidth.W)))
  val busy        = RegInit(false.B)
  val resultValid = RegInit(false.B)

  io.out.valid := resultValid
  io.in.ready  := true.B
  // useless signals
  io.pb     := DontCare
  io.pb.we  := false.B
  io.pb.din := 0.U
  errIn(1)  := 0.S // below right err is always zero

  // variables
  val diffBelow      = pos + config.imageCol.U
  val diffBelowLeft  = pos + (config.imageCol - 1).U
  val (cnt, cntWrap) = Counter(busy && !resultValid, CycleNr)

  // Emit outputs
  io.out.bits.pos   := pos
  io.out.bits.pix   := pix
  io.out.bits.errIn := errIn

  /** Read right pixel error once there is a handshake */
  io.pb.en   := io.in.fire
  io.pb.addr := io.in.bits.pos + 1.U // read right pos

  errIn(0) := Mux(config.isLastColumn(pos) || config.isFirstRow(pos), 0.S, io.pb.dout.asSInt)
  /** Get read errors from TC by Forwarding */
  errIn(2) := Mux(config.isFirstColumn(pos) || config.isLastRow(pos), 0.S, io.err_r(0))
  errIn(3) := Mux(config.isFirstColumn(pos) || config.isLastRow(pos), 0.S, io.err_r(1))

  when(cntWrap && !io.stop) { resultValid := true.B }
  when(io.out.fire) {
    resultValid := false.B
  }
  when(io.in.fire) {
    val inBundle = io.in.deq()
    pos  := inBundle.pos
    pix  := inBundle.pix
    busy := true.B
  }
}
